/*
 * Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved.
 *
 * Licensed under the OpenSSL license (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
 * in the file LICENSE in the source distribution or at
 * https://www.openssl.org/source/license.html
 */

/*
 * Simple AES CCM test program, uses the same NIST data used for the FIPS
 * self test but uses the application level EVP APIs.
 */
#include <stdio.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <sys/stat.h>


//ecb
//pkcs5padding
//return 0 failed
int aes_ecb_encrypt(unsigned char *cipherText, int *cipherTextLen, const unsigned char *plaintext,
                    int plaintextLen,
                    const unsigned char *key,
                    const unsigned char *iv) {
    int encLen = 0;
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    int result;
    result = EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, iv);
    if(!result)
        goto end;
    result = EVP_EncryptUpdate(ctx, cipherText, cipherTextLen, plaintext, plaintextLen);
    if(!result)
        goto end;
    encLen = *cipherTextLen;
    result = EVP_EncryptFinal(ctx, cipherText + (*cipherTextLen), cipherTextLen);
    if(!result)
        goto end;
    encLen += *cipherTextLen;
    *cipherTextLen = encLen;
    end:
    EVP_CIPHER_CTX_free(ctx);
    return result;
}

//return 0 failed
int aes_ecb_decrypt(unsigned char *plaintext, int *plaintextLen, const unsigned char *cipherText,
                    int cipherTextLen,
                    const unsigned char *key,
                    const unsigned char *iv) {
    int decLen = 0;
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    int result;
    result = EVP_DecryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, iv);
    if(!result)
        goto end;
    result = EVP_DecryptUpdate(ctx, plaintext, plaintextLen, cipherText, cipherTextLen);
    if(!result)
        goto end;

    decLen = *plaintextLen;
    result = EVP_DecryptFinal(ctx, plaintext + (*plaintextLen), plaintextLen);
    if(!result)
        goto end;
    decLen += *plaintextLen;
    *plaintextLen = decLen;
    plaintext[*plaintextLen] = '\0';

    end:
    EVP_CIPHER_CTX_free(ctx);
    return result;
}


int getFileSize(const char* filename) {
    struct stat statBuf;
    stat(filename, &statBuf);
    int size = statBuf.st_size;
    return size;
}

void getFileBytes(const char* file, char* buf, int* bufSize){
    FILE* fp = fopen(file, "rb");
    if(fp == NULL)
        return;
    int readLen, totalSize = 0;
    while((readLen = fread(buf, 1, 0x4000, fp))>0){
        buf += readLen;
        totalSize += readLen;
    }
    *bufSize = totalSize;
    fclose(fp);
}

static void byte2Hex(char* hex, char* bytes, int bytelen){
    char digit[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    for(int i=0; i<bytelen; i++){
        hex[i*2] = digit[bytes[i]>>4 & 0X0F];
        hex[i*2+1] = digit[bytes[i] & 0X0F];
    }
    hex[bytelen*2] ='\0';
}

static void hex2Byte(char* byte, char* hex, int hexlen){
    int destlen = hexlen/2;
    for(int i=0; i<destlen; i++){
        sscanf(hex, "%02X", byte);
        hex+=2;
        byte++;
    }

}

int encryptBigFile(const char *cipherFile, const char *plaintFile,
                   const unsigned char *key,
                   const unsigned char *iv){
    FILE* srcfp= fopen(plaintFile, "rb");
    if(srcfp == NULL)
        return 0;
    FILE* destfp= fopen(cipherFile, "wb");
    size_t READ_BUF_SIZE = 0X400;
    size_t ENCRYPTO_BUF_SIZE = READ_BUF_SIZE * 16;//adjust du to encrypto type
    char* readBuf = malloc(sizeof(char) * READ_BUF_SIZE);
    char* encryptBuf = malloc(sizeof(char) * ENCRYPTO_BUF_SIZE);
    memset(encryptBuf, 0, sizeof(char) * ENCRYPTO_BUF_SIZE);
    memset(readBuf, 0, sizeof(char) * READ_BUF_SIZE);
    int readLen, encryptLen, itemEncryptLen;
    while((readLen = fread(readBuf, 1, READ_BUF_SIZE, srcfp))>0){
        aes_ecb_encrypt(encryptBuf, &encryptLen, readBuf, readLen, key, iv);
        if(itemEncryptLen == 0)
            itemEncryptLen = encryptLen;
        fwrite(encryptBuf, encryptLen, 1, destfp);
        printf("encrypto: readLen:%d, encryptLen:%d \n", readLen, encryptLen);
    }

    free(readBuf);
    free(encryptBuf);
    fclose(srcfp);
    fclose(destfp);
    return itemEncryptLen;
}

void decryptBigFile(const char *plaintFile, const char *cipherFile, int readBufSize,
                    const unsigned char *key,
                    const unsigned char *iv){
    FILE* srcfp= fopen(cipherFile, "rb");
    if(srcfp == NULL)
        return;
    FILE* destfp= fopen(plaintFile, "wb");
    char* readBuf = malloc(sizeof(char) * readBufSize);
    char* decryptBuf = malloc(sizeof(char) * readBufSize);
    memset(decryptBuf, 0, sizeof(char) * readBufSize);
    memset(readBuf, 0, sizeof(char) * readBufSize);
    int readLen, decryptLen;
    while((readLen = fread(readBuf, 1, readBufSize, srcfp))>0){
        aes_ecb_decrypt(decryptBuf, &decryptLen, readBuf, readLen, key, iv);
        fwrite(decryptBuf, decryptLen, 1, destfp);
        printf("decrypto: readLen:%d,  decryptoLen::%d \n", readLen, decryptLen);
    }
    free(readBuf);
    free(decryptBuf);
    fclose(srcfp);
    fclose(destfp);
}

int encryptFile(const char *cipherFile, const char *plaintFile,
                const unsigned char *key, const unsigned char *iv){
    int size = getFileSize(plaintFile);
    if(size <= 0)
        return 0;
    char* buf = malloc(sizeof(char) * size);
    char* enbuf = malloc(sizeof(char) * size *16);
    int bufSize, encryptLen;
    getFileBytes(plaintFile, buf, &bufSize);
    int result;
    result = aes_ecb_encrypt(enbuf, &encryptLen, buf, bufSize, key, iv);
    if(!result)
        goto end;
    FILE* cipherFp= fopen(cipherFile, "wb");
    fwrite(enbuf, encryptLen, 1, cipherFp);
    free(buf);
    free(enbuf);
    fclose(cipherFp);
    end:
    return result;
}

int decryptFile(const char *plaintFile, const char *cipherFile,
                const unsigned char *key, const unsigned char *iv){
    int size = getFileSize(cipherFile);
    printf("file size:%d\n", size);
    if(size <= 0)
        return 0;
    char* buf = malloc(sizeof(char) * size);
    char* debuf = malloc(sizeof(char) * size);
    int bufSize, decryptLen;
    getFileBytes(cipherFile, buf, &bufSize);
    int result = aes_ecb_decrypt(debuf, &decryptLen, buf, bufSize, key, iv);
    if(!result)
        goto end;
    FILE* plaintFp= fopen(plaintFile, "wb");
    fwrite(debuf, decryptLen, 1, plaintFp);
    free(buf);
    free(debuf);
    fclose(plaintFp);
    end:
    return result;
}


//int main(int argc, char **argv)
//{
//    unsigned char* inStr = "0123456789abcdef";
//    int inLen = strlen(inStr);
//    unsigned char outbuf[1024];
//    int outlen = 0;
//    int ret = aes_ecb_encrypt(outbuf, &outlen, inStr, inLen, key, iv);
//    char hex[outlen * 2 + 1];
//    byte2Hex(hex, outbuf, outlen);
//
//    printf("result:%d ==> encryptLen:%d hexlen:%d, %s\n", ret, outlen, strlen(hex), hex);
//
//    char destr[outlen];
//    int destrlen;
//    char bytes[outlen];
//    hex2Byte(bytes, hex, strlen(hex));
//    int result = aes_ecb_decrypt(destr, &destrlen, bytes, strlen(hex)/2, key, iv);
//    printf("result:%d => destrlen:%d, byteLen:%d %s\n", result, destrlen, strlen(hex)/2, destr);
//
///************************************************************************************************************************/
//    int a = encryptFile("/home/tys/qt-project/aes/as_en.png", "/home/tys/qt-project/aes/as.png", key, iv);
//    printf("encryptFile result:%d\n", a);
//
//    a = decryptFile("/home/tys/qt-project/aes/as_de.png", "/home/tys/qt-project/aes/as_en.png", key, iv);
//    printf("decryptFile result:%d\n", a);
//
//    printf("---------------------------------");
//}
